package com.hxkj.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.duijiekeji.openapi.core.DuijieOpenApiFactory;
import com.duijiekeji.openapi.core.OpenApiFactory;
import com.duijiekeji.openapi.core.communicate.RequestAndResponseProcessor;
import com.duijiekeji.openapi.core.mutual.NotifyContent;
import com.duijiekeji.openapi.core.mutual.ResponseContent;
import com.duijiekeji.openapi.core.security.ErrorSignatureException;
import com.duijiekeji.openapi.core.util.QueryStringUtils;
import com.hxkj.client.service.OrderClient;
import com.hxkj.client.service.WithdrawalClient;
import com.hxkj.dao.CommonPaymentDao;
import com.hxkj.dao.CommonWithdrawDao;
import com.hxkj.domain.bo.PayParameter;
import com.hxkj.domain.bo.WithdrawParameter;
import com.hxkj.domain.enums.BankEnum;
import com.hxkj.domain.enums.PaymentStatusEnum;
import com.hxkj.domain.po.CommonPayment;
import com.hxkj.domain.po.CommonWithdraw;
import com.hxkj.lottery.domain.po.CompanyWithdrawal;
import com.hxkj.lottery.domain.po.RechargeOrder;
import com.hxkj.mutual.LoanRequestContent;
import com.hxkj.mutual.RechargeBuildRequestContent;
import com.hxkj.service.KuaijiePayService;
import com.hxkj.utils.UUIDUtil;
import com.platform.common.domain.exception.CustomMessageException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * Create by chenwei on 2019/1/14 16:04
 */
@Service
@Slf4j
public class KuaijiePayServiceImpl implements KuaijiePayService {
    // 异步通知url
    @Value("${pay.kuaijie.rechargeNotifyUrl}")
    private String RECHARGE_NOTIFY_URL;
    // 异步通知url
    @Value("${pay.kuaijie.withdrawNotifyUrl}")
    private String WITHDRAW_NOTIFY_URL;
    // 商户号
    private final static String PARTNER_ID = "101711701iv0n4q2q400";

    // 用户号下用户号
    private final static String USER_ID = "450146";

    // 业务方订单支付成功状态
    private final static String PAY_SUCCESS = "15";

    @Resource
    private CommonPaymentDao commonPaymentDao;

    @Resource
    private RequestAndResponseProcessor processor;

    @Resource
    private OpenApiFactory openApiFactory;

    @Resource
    private OrderClient orderClient;

    @Resource
    private WithdrawalClient withdrawalClient;

    @Resource
    private CommonWithdrawDao commonWithdrawDao;

    @Override
    public String pay(PayParameter payParameter) throws Exception {
        Long now = System.currentTimeMillis();
        // 判断订单是否被创建
        List<CommonPayment> commonPayments = commonPaymentDao.get(CommonPayment.builder().orderId(payParameter.getId()).build());
        // 没有创建订单
        if (commonPayments.isEmpty()) {
            // 生成本地支付订单
            String id = UUIDUtil.generateUUID();
            CommonPayment commonPayment = CommonPayment.builder().id(id).amount(payParameter.getAmount())
                    // 业务订单号
                    .orderId(payParameter.getId())
                    // 商品名称
                    .product(payParameter.getName())
                    // 创建时间
                    .createTime(now)
                    // 更新时间
                    .updateTime(now)
                    // 订单状态
                    .status(PaymentStatusEnum.NON_PAYMENY.getType())
                    // 渠道
                    .payChannel(payParameter.getPayType())
                    .build();
            commonPaymentDao.save(commonPayment);
            // 构建支付请求 修改业务单号为支付单号
            payParameter.setId(id);
            // 生成支付URL
            return generatePaymentOrder(payParameter);
            // 已经创建了支付订单
        } else {
            // 判断订单状态,若支付,则返回已支付,若未支付,则继续支付
            CommonPayment commonPayment = commonPayments.get(0);
            // 若该订单未支付 继续支付
            if (StringUtils.equals(commonPayment.getStatus(), PaymentStatusEnum.NON_PAYMENY.getType())) {
                payParameter.setId(commonPayment.getId());
                return generatePaymentOrder(payParameter);
            } else {
                // 关闭或者已支付则不继续支付
                throw new CustomMessageException(400, "当前订单已经被支付");

            }
        }
    }

    @Override
    public String payCallback() throws Exception {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
//        HttpServletResponse httpServletResponse = attributes.getResponse();
        Map params = request.getParameterMap();
        log.info("进入充值回调:{}", JSONObject.toJSONString(params));
        NotifyContent nc = null;
        try {
            // 使用接收内容转换器解析通知内容
            nc = processor.getReceiveTransformer().toNotify(params, NotifyContent.class);
        } catch (ErrorSignatureException e) {
            // 如果接收到通知的时候签名校验失败
            log.error("签名校验失败");
        }
        System.out.println("通知内容：" + nc);
        switch (nc.getCurrentStatus()) {
            case SUCCESS:
                // 成功处理
                List<CommonPayment> commonPayments = commonPaymentDao.get(CommonPayment.builder().id(nc.getOrderNo()).build());
                // 验证订单是否存在
                if (!commonPayments.isEmpty()) {
                    CommonPayment commonPayment = commonPayments.get(0);
                    // 验证订单是否被处理
                    if (!commonPayment.getStatus().equals(PaymentStatusEnum.NON_PAYMENY.getType())) {
                        // 通知顺畅支付
                        log.info("订单:{}已经被处理,将要通支付方", commonPayment.getOrderId());
//                        httpServletResponse.
                        return "success";
                    }
                    // 否则修改订单状态为支付成功
                    commonPayment.setUpdateTime(System.currentTimeMillis());
                    commonPayment.setTradeStatus("TRADE_SUCCESS");
                    commonPayment.setPayId(nc.getOrderNo());
                    commonPayment.setBuyerPayAmount(commonPayment.getAmount());
                    commonPayment.setStatus(PaymentStatusEnum.PAIED.getType());
                    commonPaymentDao.update(commonPayment);

                    // ------ 通知业务调用方
                    RechargeOrder rechargeOrder = RechargeOrder.builder()
                            .orderId(commonPayment.getOrderId())
                            .build();
                    orderClient.payCallback(rechargeOrder);
                    log.debug("订单:{},已经通知业务方", nc.getOrderNo());
                    // 通知业务方
                    return "success";
                } else {
                    log.error("异步回调找不到订单:,订单号为:{}", nc.getOrderNo());
                    return "fail";
                }
            case FAIL:
                // 失败处理
                System.out.println("失败");
                break;
            case PROCESSING:
                // 中间状态处理中，需要等通知或者重新获取状态
                System.out.println("处理中");
                break;
        }
        return "fail";
    }

    @Override
    public void withdraw(WithdrawParameter withdrawParameter) {
        if (withdrawParameter.getPayType() == null) {
            throw new CustomMessageException("支付方式不能为空");
        }
        // 若提现金额小于1元
        if (withdrawParameter.getAmount().compareTo(new BigDecimal("1.00")) == -1){
            log.warn("最低提现金额为1元");
            throw new CustomMessageException("最低提现金额为1元");
        }
        Long now = System.currentTimeMillis();
        CommonWithdraw query = CommonWithdraw.builder()
                .orderId(withdrawParameter.getOrderId())
                // 提现成功
                .status("0")
                .build();
        List<CommonWithdraw> commonWithdraws = commonWithdrawDao.get(query);
        if (commonWithdraws.size() > 0) {
            throw new CustomMessageException("该订单已经提现成功");
        }
        // 支付订单id
        String id = UUIDUtil.generateUUID();
        CommonWithdraw commonWithdraw = CommonWithdraw.builder()
                .id(id)
                .orderId(withdrawParameter.getOrderId())
                .createTime(now)
                .updateTime(now)
                .accountName(withdrawParameter.getAccountName())
                // 私有账户
                .accountType("PRIVATE")
                .bankId(withdrawParameter.getBankId())
                .bankName(BankEnum.getDescriptionByCode(withdrawParameter.getBankId()))
                .amount(withdrawParameter.getAmount())
                .certNo(withdrawParameter.getCertNo())
                .accountNo(withdrawParameter.getAccountNo())
                .channel("kuaijie")
                // 2为处理中
                .status("2")
                .build();
        commonWithdrawDao.save(commonWithdraw);


        LoanRequestContent request = new LoanRequestContent();
        request.setPartnerId(PARTNER_ID);
        request.setServiceName("RechargeLoan");
        // 每次必须不同
        request.setOrderNo(id);
        request.setSecurityType("certificate");
        request.setAmount(withdrawParameter.getAmount().toString());
        request.setAccountName(withdrawParameter.getAccountName());
        request.setAccountNo(withdrawParameter.getAccountNo());
        request.setAccountType("PRIVATE");
        request.setBankId(withdrawParameter.getBankId());
        request.setNotifyUrl(WITHDRAW_NOTIFY_URL);

        ResponseContent response = null;
        try {
            response = processor.send(request, ResponseContent.class);
        } catch (ErrorSignatureException e) {
            // 如果接收到响应的时候签名校验失败
            System.out.println("签名校验失败");
        }

        log.info("响应内容：" + response);
        switch (response.getCurrentStatus()) {
            case SUCCESS:
                // 成功处理
                System.out.println("成功");
                break;
            case FAIL:
                // 失败处理
                handFailureWithdrawOrder(commonWithdraw,response);
                // 返回给调用方
                throw new CustomMessageException(response.getDescription());
            case PROCESSING:
                // 中间状态处理中，需要等通知或者重新获取状态
                System.out.println("处理中");
                break;
        }

    }


    /**
     * 处理提现发起失败
     * @param commonWithdraw
     * @param response
     */
    protected void handFailureWithdrawOrder(CommonWithdraw commonWithdraw,ResponseContent response){
        commonWithdraw.setUpdateTime(System.currentTimeMillis());
        // 第三方接口响应状态
        commonWithdraw.setTradeStatus(response.getCurrentStatus().getMessage());
        // 第三方接口响应信息
        commonWithdraw.setResultDetail(response.getDescription());

        // 业务方通知参数
        CompanyWithdrawal companyWithdrawal = CompanyWithdrawal.builder()
                .withdrawalId(commonWithdraw.getOrderId())
                .build();

        // 设置订单为提现失败
        commonWithdraw.setStatus("1");
        companyWithdrawal.setOperateStatus(1);
        companyWithdrawal.setRemark(response.getDescription());

        // 更新支付订单状态
        commonWithdrawDao.update(commonWithdraw);
    }



    @Async
    protected void listenResponse() {
        // 发起提现
        // 可以通过RequestPreProcessor与ResponsePostProcessor进行自定义的一些报文处理，或者用来打印报文日志
        processor.getRequestPreProcessors().add(content -> {
            log.info("提现请求报文：{}" ,content);
            return content;
        });
        processor.getResponsePostProcessors().add(content -> {
            log.info("提现响应报文：{}" ,content);
            return content;
        });
    }

    @Override
    public String withdrawCallback() throws Exception {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Map params = request.getParameterMap();
        log.info("进入提现回调:{}", JSONObject.toJSONString(params));
        NotifyContent nc = null;
        try {
            // 使用接收内容转换器解析通知内容
            nc = processor.getReceiveTransformer().toNotify(params, NotifyContent.class);
        } catch (ErrorSignatureException e) {
            // 如果接收到通知的时候签名校验失败
            log.error("签名校验失败");
        }
        log.info("通知内容：{}", nc);

        // 成功处理
        List<CommonWithdraw> commonWithdraws = commonWithdrawDao.get(CommonWithdraw.builder().id(nc.getOrderNo()).build());
        // 验证订单是否存在
        if (!commonWithdraws.isEmpty()) {
            CommonWithdraw commonWithdraw = commonWithdraws.get(0);
            // 若该笔订单已经提现成功
            if (StringUtils.equals(commonWithdraw.getStatus(), "0")) {
                log.info("订单:{}已经被处理,将要通支付方", commonWithdraw.getOrderId());
                return "success";
            }
            commonWithdraw.setUpdateTime(System.currentTimeMillis());
            // 第三方接口响应状态
            commonWithdraw.setTradeStatus(nc.getCurrentStatus().getMessage());
            // 第三方接口响应信息
            commonWithdraw.setResultDetail(nc.getDescription());

            // 业务方通知参数
            CompanyWithdrawal companyWithdrawal = CompanyWithdrawal.builder()
                    .withdrawalId(commonWithdraw.getOrderId())
                    .build();

            switch (nc.getCurrentStatus()) {
                case SUCCESS:
                    // 设置订单为提现成功
                    commonWithdraw.setStatus("0");
                    companyWithdrawal.setOperateStatus(0);
                    companyWithdrawal.setRemark(nc.getDescription());
                    break;
                case FAIL:
                    // 设置订单为提现失败
                    commonWithdraw.setStatus("1");
                    companyWithdrawal.setOperateStatus(1);
                    companyWithdrawal.setRemark(nc.getDescription());
                    break;
                case PROCESSING:
                    log.info("订单:【{}】正在处理中", nc.getOrderNo());
                    return "fail";
            }

            // 更新支付订单状态
            commonWithdrawDao.update(commonWithdraw);

            // ------ 通知业务调用方
            withdrawalClient.remitCallback(companyWithdrawal);
            log.debug("订单:【{}】进入提现回调,已经通业务方:{}", commonWithdraw.getOrderId());
            // 通知业务方
            return "success";
        }
        if (commonWithdraws.isEmpty()) {
            log.error("提现异步回调找不到订单:订单号为:{}", nc.getOrderNo());
            return "success";
        }

        return "fail";
    }


    /**
     * 生成支付URL
     *
     * @param payParameter
     * @throws Exception
     */
    private String generatePaymentOrder(PayParameter payParameter) throws Exception {
        RechargeBuildRequestContent buildRequest = new RechargeBuildRequestContent();
        // 用户编号，商户下的一个普通用户
        buildRequest.setUserId(USER_ID);
        // 签约的商户或合作商的ID,由平台分配。定长20字符-此商户号只供测试使用
        buildRequest.setPartnerId(PARTNER_ID);
        // 请求流水号，商户的每次请求必须唯一
        buildRequest.setOrderNo(payParameter.getId());
        // 金额（元），两位小数格式
        buildRequest.setAmount(payParameter.getAmount().toString());
        buildRequest.setServiceName("Recharge");
        buildRequest.setSecurityType("certificate");
        // 商户自己的参数，返回的时候返回
        // buildRequest.setContext("{userId:1,busiId:2}");
        buildRequest.setReturnUrl("http://sjpay.githubshop.com/api/bopay.Wang/netSilver");
        buildRequest.setNotifyUrl(RECHARGE_NOTIFY_URL);
        buildRequest.setProtocol("HTTP-FORM-JOSN");
        buildRequest.setServiceVersion("1.0");
        String queryString = QueryStringUtils.encodeQueryString((String) processor.getSendTransformer().toSend(buildRequest), processor.getCharset());
        String url = "http://47.74.179.3:70/aPage?" + queryString;
        log.debug("支付url:{}", url);
        return url;
    }


    @Bean
    public OpenApiFactory openApiFactory() throws IOException {
        InputStream pfx = KuaijiePayServiceImpl.class.getResourceAsStream("/915001076889005750e69da8e6968c.pfx");
        InputStream cert = KuaijiePayServiceImpl.class.getResourceAsStream("/duijie.cer");
        // 这里开始为API内容，首先获取OpenApi工厂，该工厂是线程安全的，可以全站使用
        OpenApiFactory oaf = DuijieOpenApiFactory.newInstance("https://47.74.179.3:8899/openapi/gateway", pfx,
                "HzD7ew".toCharArray(), "HzD7ew".toCharArray(), cert);
        pfx.close();
        cert.close();
        return oaf;
    }

    @Bean
    public RequestAndResponseProcessor processor(OpenApiFactory openApiFactory) throws IOException {
//        OpenApiFactory oaf = openApiFactory();
        // 通过工厂创建请求与响应处理器，该处理器是线程安全的，如果使用模式与信息等一样，则可以全站使用
        RequestAndResponseProcessor processor = openApiFactory.newProcessor();
        return processor;
    }


}
